#include "platform.h"
#include "keyboard.h"
#include "conf.h"
#include "histogram.h"
#include "font.h"
#include "camera.h"
#include "gui_draw.h"
#include "gui_osd.h"
#include "core.h"
#include "stdlib.h"

//-------------------------------------------------------------------
#define CONF_FILE  "A/CHDK/CCHDK.CFG"
#define CONF_MAGICK_VALUE (0x33204741)

#define CONF_INFO(id, param, type, def, func) { id, sizeof( param ), &param , type, {def}, func }

//-------------------------------------------------------------------
typedef struct {
    unsigned short      id;
    unsigned short      size;
    void                *var;
    int                 type;
    union {
        void            *ptr;
        int             i;
        color           cl;
    };
    void                (*func)();
} ConfInfo;

//-------------------------------------------------------------------
Conf conf;

int state_shooting_progress = SHOOTING_PROGRESS_NONE;
int state_expos_recalculated;
int state_expos_under;
int state_expos_over;
int auto_started;

// reyalp: putting these in conf, since the conf values are lookups for them
// prefixes and extentions available for raw images (index with conf.raw_prefix etc)
const char* img_prefixes[NUM_IMG_PREFIXES]={ "IMG_", "CRW_", "SND_" /*, "AUT_", "ETC_","SDR_", "MVI_", "MRK_"*/};
const char* img_exts[NUM_IMG_EXTS]={ ".JPG", ".CRW", ".CR2", ".THM", ".WAV"/*, ".AVI", ".MRK"*/};
// ugh

//-------------------------------------------------------------------
static int def_ubasic_vars[SCRIPT_NUM_PARAMS] = {0};
static int def_batt_volts_max, def_batt_volts_min;
static OSD_pos def_histo_pos, def_batt_icon_pos, def_batt_txt_pos, def_space_txt_pos, 
               def_mode_ev_pos, def_values_pos, def_clock_pos;

static void conf_change_script_file();
static void conf_change_histo_mode();
static void conf_change_histo_layout();
static void conf_change_font_cp();
static void conf_change_menu_rbf_file();
static void conf_change_menu_symbol_rbf_file();
static void conf_change_alt_mode_button();
static void conf_change_grid_file();
static void conf_change_dng_ext();



void clear_values()
{	
	if (conf.platformid != PLATFORMID) // the following config entries will be resetted if you switch the camera using the same cfg
	{
    conf.fast_ev = 0;
    conf.platformid = PLATFORMID;
	}

}

static const ConfInfo conf_info[] = {
/* !!! Do NOT change ID for items defined already! Append a new one at the end! !!! */
    CONF_INFO(  4, conf.show_histo,                 CONF_VALUE,         i:0, NULL),
    CONF_INFO(  8, conf.batt_volts_max,             CONF_VALUE_PTR,     ptr:&def_batt_volts_max, NULL),
    CONF_INFO(  9, conf.batt_volts_min,             CONF_VALUE_PTR,     ptr:&def_batt_volts_min, NULL),
    CONF_INFO( 10, conf.batt_step_25,               CONF_VALUE,         i:1, NULL),
    CONF_INFO( 11, conf.batt_perc_show,             CONF_VALUE,         i:1, NULL),
    CONF_INFO( 13, conf.batt_icon_show,             CONF_VALUE,         i:1, NULL),
    CONF_INFO( 16, conf.show_overexp,               CONF_VALUE,         i:1, NULL),
    CONF_INFO( 17, conf.histo_mode,                 CONF_VALUE,         i:HISTO_MODE_LINEAR, conf_change_histo_mode),
    CONF_INFO( 19, conf.histo_ignore_boundary,      CONF_VALUE,         i:4, NULL),
    CONF_INFO( 20, conf.histo_layout,               CONF_VALUE,         i:OSD_HISTO_LAYOUT_A, conf_change_histo_layout),
    CONF_INFO( 21, conf.histo_pos,                  CONF_OSD_POS_PTR,   ptr:&def_histo_pos, NULL),
    CONF_INFO( 23, conf.batt_icon_pos,              CONF_OSD_POS_PTR,   ptr:&def_batt_icon_pos, NULL),
    CONF_INFO( 24, conf.batt_txt_pos,               CONF_OSD_POS_PTR,   ptr:&def_batt_txt_pos , NULL),
    CONF_INFO( 26, conf.values_pos,                 CONF_OSD_POS_PTR,   ptr:&def_values_pos , NULL),
    CONF_INFO( 27, conf.histo_color,                CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_WHITE), NULL),
    CONF_INFO( 28, conf.osd_color,                  CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_FG), NULL),
//    CONF_INFO( 29, conf.batt_icon_color,            CONF_VALUE,         cl:COLOR_WHITE, NULL),
    CONF_INFO( 30, conf.menu_color,                 CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_FG), NULL),
    CONF_INFO( 33, conf.flashlight,                 CONF_VALUE,         i:0, NULL),
//    CONF_INFO( 34, conf.ns_enable_memdump,          CONF_VALUE,         i:0, NULL),
    CONF_INFO( 42, conf.clock_pos,                  CONF_OSD_POS_PTR,   ptr:&def_clock_pos , NULL),
    CONF_INFO( 47, conf.splash_show,                CONF_VALUE,         i:1, NULL),
    CONF_INFO( 48, conf.histo_color2,               CONF_VALUE,         cl:MAKE_COLOR(COLOR_RED, COLOR_WHITE), NULL),
    CONF_INFO( 62, conf.menu_symbol_enable,         CONF_VALUE,         i:1, NULL),
    CONF_INFO( 63, conf.alt_mode_button,            CONF_VALUE,         i:KEY_DISPLAY, conf_change_alt_mode_button),
    CONF_INFO( 64, conf.lang_file,                  CONF_CHAR_PTR,      ptr:"A/CHDK/LANG/german.lng", NULL),
    CONF_INFO( 65, conf.font_cp,                    CONF_VALUE,         i:FONT_CP_WIN_1252, conf_change_font_cp),
    CONF_INFO( 66, conf.menu_rbf_file,              CONF_CHAR_PTR,      ptr:"", conf_change_menu_rbf_file),
    CONF_INFO( 67, conf.alt_prevent_shutdown,       CONF_VALUE,         i:ALT_PREVENT_SHUTDOWN_ALT, conf_update_prevent_shutdown),
        

        
    
    CONF_INFO(129, conf.histo_show_ev_grid,         CONF_VALUE,         i:0, NULL),
    
    CONF_INFO(130, conf.osd_color_warn,             CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_RED), NULL),
    
    CONF_INFO(155, conf.clock_format,               CONF_VALUE,         i:0, NULL),
    CONF_INFO(156, conf.clock_indicator,            CONF_VALUE,         i:0, NULL),
    

    CONF_INFO(165, conf.menu_title_color,           CONF_VALUE,         cl:MAKE_COLOR(COLOR_WHITE, COLOR_BLACK), NULL),
    CONF_INFO(166, conf.menu_cursor_color,          CONF_VALUE,         cl:MAKE_COLOR(COLOR_SELECTED_BG, COLOR_SELECTED_FG), NULL),
    CONF_INFO(167, conf.menu_center,                CONF_VALUE,         i:1, NULL),
    CONF_INFO(171, conf.osd_color_override,         CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_RED), NULL),
    CONF_INFO(174, conf.hide_osd,                   CONF_VALUE,         i:1, NULL),
    CONF_INFO(180, conf.fast_ev,                    CONF_VALUE,         i:0, NULL),
    CONF_INFO(181, conf.fast_ev_step,               CONF_VALUE,         i:1, NULL),
    CONF_INFO(183, conf.menu_symbol_rbf_file,       CONF_CHAR_PTR,      ptr:"A/CHDK/SYMBOLS/icon_10.rbf", conf_change_menu_symbol_rbf_file),
    CONF_INFO(184, conf.menu_symbol_color,          CONF_VALUE,         cl:MAKE_COLOR(COLOR_BG, COLOR_FG), NULL),
    CONF_INFO(201, conf.menu_select_first_entry,    CONF_VALUE,         i:1, NULL),
    CONF_INFO(208, conf.start_sound,                CONF_VALUE,         i:0, NULL),
    CONF_INFO(229, conf.platformid,                 CONF_VALUE,         i:PLATFORMID, NULL),
    CONF_INFO(246, conf.sel_frame,                  CONF_VALUE,         i:1, NULL),
    CONF_INFO(247, conf.save_frame_info,            CONF_VALUE,         i:0, NULL),
    CONF_INFO(248, conf.expo_type,                  CONF_VALUE,         i:0, NULL),
    };
#define CONF_NUM (sizeof(conf_info)/sizeof(conf_info[0]))

//-------------------------------------------------------------------
static void conf_change_histo_mode() {
    histogram_set_mode(conf.histo_mode);
}

static void conf_change_histo_layout() {
    if (conf.histo_layout==OSD_HISTO_LAYOUT_Y || conf.histo_layout==OSD_HISTO_LAYOUT_Y_argb) {
        histogram_set_main(HISTO_Y);
    } else {
        histogram_set_main(HISTO_RGB);
    }
}

static void conf_change_font_cp() {
    font_init();
    font_set(conf.font_cp);
}

static void conf_change_menu_rbf_file() {
    if (!rbf_load(conf.menu_rbf_file))
        rbf_load_from_8x16(current_font);
    rbf_set_codepage(FONT_CP_WIN);
}

static void conf_change_menu_symbol_rbf_file() {
    if(!rbf_load_symbol(conf.menu_symbol_rbf_file)) conf.menu_symbol_enable=0;		//AKA
}

static void conf_change_alt_mode_button() {
#if CAM_ADJUSTABLE_ALT_BUTTON
    kbd_set_alt_mode_key_mask(conf.alt_mode_button);
#else
    conf.alt_mode_button = KEY_DISPLAY;
#endif
}


/*
update the prevent display off/prevent shutdown based on current state
doesn't really belong in conf but not clear where else it should go
*/
void conf_update_prevent_shutdown(void) {
	if(conf.alt_prevent_shutdown == ALT_PREVENT_SHUTDOWN_ALWAYS 
		|| (conf.alt_prevent_shutdown == ALT_PREVENT_SHUTDOWN_ALT && gui_get_mode() != GUI_MODE_NONE)
		|| (conf.alt_prevent_shutdown == ALT_PREVENT_SHUTDOWN_ALT_SCRIPT && state_kbd_script_run)) {
        disable_shutdown();
	} else {
        enable_shutdown();
	}
}

//-------------------------------------------------------------------
static void conf_init_defaults() {
    // init some defaults values
    def_batt_volts_max = get_vbatt_max();
    def_batt_volts_min = get_vbatt_min();
    def_histo_pos.x = 45;
    def_histo_pos.y = vid_get_bitmap_screen_height()-HISTO_HEIGHT-40;
    def_batt_icon_pos.x = 178;
    def_batt_icon_pos.y = 0;
    def_batt_txt_pos.x=178;
    def_batt_txt_pos.y=1*FONT_HEIGHT;
    def_mode_ev_pos.x=vid_get_bitmap_screen_width()-40*FONT_WIDTH-2;
    def_mode_ev_pos.y=vid_get_bitmap_screen_height()-8*FONT_HEIGHT-2;
    def_values_pos.x=vid_get_bitmap_screen_width()-12*FONT_WIDTH;
    def_values_pos.y=30;
    def_clock_pos.x=vid_get_bitmap_screen_width()-5*FONT_WIDTH-2;
    def_clock_pos.y=0;

}

//-------------------------------------------------------------------
void conf_load_defaults() {
    register int i;

    for (i=0; i<CONF_NUM; ++i) {
        switch (conf_info[i].type) {
            case CONF_VALUE:
                memcpy(conf_info[i].var, &(conf_info[i].i), conf_info[i].size);
                break;
            default:
                memcpy(conf_info[i].var, conf_info[i].ptr, conf_info[i].size);
                break;
        }
        if (conf_info[i].func) {
            conf_info[i].func();
        }
    }
}

//-------------------------------------------------------------------
void conf_save() {
    static const long t=CONF_MAGICK_VALUE;
    register int i;
    int fd;
    char *buf=umalloc(sizeof(t)+CONF_NUM*(sizeof(conf_info[0].id)+sizeof(conf_info[0].size))+sizeof(conf));
    char *p=buf;

    fd = open(CONF_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0777); 
    if (fd>=0){
        memcpy(p, &t, sizeof(t));
        p+=sizeof(t);
        for (i=0; i<CONF_NUM; ++i) {
            memcpy(p, &(conf_info[i].id), sizeof(conf_info[i].id));
            p+=sizeof(conf_info[i].id);
            memcpy(p, &(conf_info[i].size), sizeof(conf_info[i].size));
            p+=sizeof(conf_info[i].size);
            memcpy(p, conf_info[i].var, conf_info[i].size);
            p+=conf_info[i].size;
        }

        write(fd, buf, p-buf);
        close(fd);
    }
	ufree(buf);
}

//-------------------------------------------------------------------
void conf_restore() {
    int fd, rcnt, i;
    unsigned short id, size;
    char *buf;
    int offs;
    struct stat st;

    conf_init_defaults();

    conf_load_defaults();

    if( stat(CONF_FILE,&st) != 0 || st.st_size < sizeof(int))
        return;

    if(!(buf=umalloc(st.st_size)))
        return;

    fd = open(CONF_FILE, O_RDONLY, 0777); 
    if( fd < 0 ) {
        ufree(buf);
        return;
    }
    
    rcnt = read(fd,buf,st.st_size);
    close(fd);
    // read magick value
    if (*(int *)buf!=CONF_MAGICK_VALUE || rcnt != st.st_size) {
        ufree(buf);
        return;
    }
    offs=sizeof(int);
    while (1) {
        if (offs + sizeof(short) > rcnt)
            break;
        id=*((short *)(buf + offs));
        offs += sizeof(short);

        if (offs + sizeof(short) > rcnt)
            break;
        size=*((short *)(buf + offs));
        offs += sizeof(short);

        for (i=0; i<CONF_NUM; ++i) {
            if (conf_info[i].id==id && conf_info[i].size==size) {
                if (offs + size <= rcnt) {
                   memcpy(conf_info[i].var, buf+offs, size);
                   if (conf_info[i].func) {
                       conf_info[i].func();
                   }
                }
                offs += size;
                break;
            }
        }
        if (i == CONF_NUM) { // unknown id, just skip data
            offs += size;
        }
    }
    ufree(buf);
    // clear any "clear on restart" values
    clear_values();
}

//-------------------------------------------------------------------
int conf_getValue(unsigned short id, tConfigVal* configVal) {
    unsigned short i;
    int ret = CONF_EMPTY;
    OSD_pos* pos;
    
    if( id==0 ) {
        configVal->numb = 0;
        for( i=0; i<CONF_NUM; ++i ) if( configVal->numb<conf_info[i].id ) configVal->numb = conf_info[i].id;
        ret = CONF_VALUE;
    } else {
        for( i=0; i<CONF_NUM; ++i ) {
            if( conf_info[i].id==id ) {
                switch( conf_info[i].type ) {
                    case CONF_VALUE:
                    case CONF_VALUE_PTR:
                        switch( conf_info[i].size ) {
                            case sizeof(int):
                                configVal->numb = *(int*)conf_info[i].var;
                                ret = CONF_VALUE;
                            break;
                            case sizeof(short):
                                configVal->numb = *(short*)conf_info[i].var;
                                ret = CONF_VALUE;
                            break;
                            case sizeof(char):
                                configVal->numb = *(char*)conf_info[i].var;
                                ret = CONF_VALUE;
                            break;
                        }
                    break;
                    case CONF_INT_PTR:
                        configVal->numb = conf_info[i].size/sizeof(int);
                        configVal->pInt = (int*)conf_info[i].var;
                        ret = CONF_INT_PTR;
                    break;
                    case CONF_CHAR_PTR:
                        configVal->str = conf_info[i].var;
                        ret = CONF_CHAR_PTR;
                    break;
                    case CONF_OSD_POS_PTR:
                        pos = (OSD_pos*)conf_info[i].var;
                        configVal->pos.x = pos->x;
                        configVal->pos.y = pos->y;
                        ret = CONF_OSD_POS_PTR;
                    break;
                }
                break;
            }
        }
    }
    return ret;
}

//-------------------------------------------------------------------
int conf_setValue(unsigned short id, tConfigVal configVal) {
    unsigned short i;
    int ret = CONF_EMPTY, len, len2;
    OSD_pos* pos;
    
    for( i=0; i<CONF_NUM; ++i ) {
        if( conf_info[i].id==id ) {
            switch( conf_info[i].type ) {
                case CONF_VALUE:
                case CONF_VALUE_PTR:
                    if( configVal.isNumb ) {
                        switch( conf_info[i].size ) {
                            case sizeof(int):
                                *(int*)conf_info[i].var = (int)configVal.numb;
                                ret = CONF_VALUE;
                            break;
                            case sizeof(short):
                                *(short*)conf_info[i].var = (short)configVal.numb;
                                ret = CONF_VALUE;
                            break;
                            case sizeof(char):
                                *(char*)conf_info[i].var = (char)configVal.numb;
                                ret = CONF_VALUE;
                            break;
                        }
                    }
                break;
                case CONF_INT_PTR:
                    if( configVal.isPInt ) {
                        len = conf_info[i].size;
                        len2 = configVal.numb*sizeof(int);
                        if( len2<len ) len = len2;
                        memcpy(conf_info[i].var, configVal.pInt, len);
                        ret = CONF_INT_PTR;
                    }
                break;
                case CONF_CHAR_PTR:
                    if( configVal.isStr ) {
                        len = strlen(configVal.str);
                        if( len>0 && len<CONF_STR_LEN) {
                            strncpy(conf_info[i].var, configVal.str ,len+1);
                        }
                        ret = CONF_CHAR_PTR;
                    }
                break;
                case CONF_OSD_POS_PTR:
                    if( configVal.isPos ) {
                        pos = (OSD_pos*)conf_info[i].var;
                        pos->x = configVal.pos.x;
                        pos->y = configVal.pos.y;
                        ret = CONF_OSD_POS_PTR;
                    }
                break;
            }
            break;
        }
    }
    if( ret!=CONF_EMPTY ) {
        if (conf_info[i].func) {
            conf_info[i].func();
        }
        conf_save();
    }
    return ret;
}
//-------------------------------------------------------------------
